/* 
 *        SBS
 *
 * Author: Suripantas Team
 * 
 */

#include <thread>

#include "Model/ObjFactory.h"
#include "Model/Object.h"
#include "Model/Rectangle.h"
#include "Model/Parallellogram.h"
#include "Model/Circle.h"
#include "Model/Polygon.h"
#include "Model/Trapezium.h"
#include "Model/Character.h"
#include "Model/SBSImage.h"
#include "Model/ContactFloorListener.h"

#include "Define/Window.h"
#include "Define/FileWindow.h"
#include "Define/DefaultValues.h"

//#include <JSON/json.hh>

using namespace std;

SBSLog* myLogFile;

void checkQuitOrReload(SDL_Event* event, bool* quit, bool* r) {
    if (event->type == SDL_QUIT) {
        *quit = true;
        *r = false;
    }
    if (event->key.keysym.sym == SDLK_r) {
        *quit = true;
        *r = true;
    }
}

bool loadLevel(int argc, char* args[]) {

    bool quit = false;
    bool r = false;
    myLogFile->getInstance(MY_LOG_MODE).setMyTimeStamp(); //graba el timestamp de la corrida 
    char* nameFileJSON = args[1]; //nombre del archivo que pasan por linea de comando.
    if (argc > 1) {
        //tengo argumentos 
        ifstream myjfile(nameFileJSON, ios::out | ios::binary);
        if (myjfile.fail()) {
            //no existe el archivo
            myLogFile->writeInLog(1, "[1] El parametro informado no se corresponde con un nombre de archivo existente.  Se cargara el escenario por default \n");
            ofstream myJSONFile;
            myJSONFile.open(nameFileJSON, ios_base::app);
            // Or build the object manually
            JSON::Object obj;

            obj["nombre"] = GAME_NAME;
            obj["gravedad"] = WORLD_GRAVITY;

            JSON::Object o, o1;

            o1["x"] = CHARACTER_POSITION_X;
            o1["y"] = CHARACTER_POSITION_Y;

            o["anchoPx"] = SCREEN_WIDTH;
            o["altoPx"] = SCREEN_HEIGHT;
            o["anchoUn"] = DEFAULT_LOGICAL_W;
            o["altoUn"] = DEFAULT_LOGICAL_H;
            o["fondo"] = SCREEN_BACKGROUND;
            o["personaje"] = o1;

            obj["escenario"] = o;

            //Objetos

            JSON::Object fig, fig1, fig2;

            fig["tipo"] = "RECTANGLE";
            fig["x"] = 50;
            fig["y"] = 100;
            fig["alto"] = 10;
            fig["ancho"] = 50;
            fig["color"] = "#AA05B8";
            fig["rot"] = 0.0;
            fig["masa"] = 2;
            fig["estatico"] = true;

            fig1["tipo"] = "RECTANGLE";
            fig1["x"] = 100;
            fig1["y"] = 200;
            fig1["alto"] = 20;
            fig1["ancho"] = 80;
            fig1["color"] = "#AA05B8";
            fig1["rot"] = 0.0;
            fig1["masa"] = 2;
            fig1["estatico"] = true;

            fig2["tipo"] = "PARALLELLOGRAM";
            fig2["x"] = 100;
            fig2["y"] = 200;
            fig2["alto"] = 20;
            fig2["ancho"] = 80;
            fig2["color"] = "#AA05B8";
            fig2["rot"] = 0.0;
            fig2["masa"] = 2;
            fig2["estatico"] = true;

            JSON::Array a;
            a.push_back(fig);
            a.push_back(fig1);
            a.push_back(fig2);

            obj["objetos"] = a;

            cout << obj << endl;
            myJSONFile << obj;
        }//fin de no existe el archivo
    }
    if (argc <= 1) {
        nameFileJSON = (char*) "Doc/configuration.json";
        myLogFile->writeInLog(0, "[0] No se recibieron parametros en la ejecucion, se cargara el juego a partir del archivo JSON default.\n");
    }
    ifstream myjfile(nameFileJSON, ios::out | ios::binary);
    if (myjfile.good()) {
        myLogFile->writeInLog(0, "[0] Se valido correctamente el archivo.  Se procede a parsear el archivo de escenario\n");
        FileWindow ventanaArch(nameFileJSON);

        Window* gWindow;
        SDL_Renderer* gRenderer = NULL;
        SBSImage* background = new SBSImage();

        SDL_Event event;

        gWindow = new Window(ventanaArch.getName(), ventanaArch.getPixelWidth(), ventanaArch.getPixelHeight(), SDL_WINDOW_SHOWN, &gRenderer);
        if (!(gWindow->isInit())) {
            myLogFile->writeInLog(2, "[2] Fallo al inicializar la ventana de SDL\n");
        } else {
            if (!(background->loadMedia(ventanaArch.getBackGround(), &gRenderer))) {
                myLogFile->writeInLog(1, "[1] Error al cargar la imagen de fondo. Se cargara la imagen default [SCREEN_BACKGROUND] \n");
                background->loadMedia(SCREEN_BACKGROUND, &gRenderer);
            }
            b2Vec2 gravity(0.0f, ventanaArch.getGravity());
            b2World world(gravity);
            ContactFloorListener contListener;
            world.SetContactListener(&contListener);

            ObjFactory objFact(world, gRenderer, ventanaArch.getPixelWidth(), ventanaArch.getPixelHeight(), ventanaArch.getLogicalUnitWidth(), ventanaArch.getLogicalUnitHeight());

            Rectangle ground(&objFact, true, (ventanaArch.getLogicalUnitWidth()) / 2, ventanaArch.getLogicalUnitHeight(), ventanaArch.getLogicalUnitWidth(), ventanaArch.getLogicalUnitHeight()*0.02, 10, 0);
            Rectangle ceiling(&objFact, true, (ventanaArch.getLogicalUnitWidth()) / 2, 0, ventanaArch.getLogicalUnitWidth(), 2, 10, 0);
            Rectangle leftWall(&objFact, true, 0, (ventanaArch.getLogicalUnitHeight()) / 2, 2, ventanaArch.getLogicalUnitHeight(), 10, 0);
            Rectangle rightWall(&objFact, true, ventanaArch.getLogicalUnitWidth(), (ventanaArch.getLogicalUnitHeight()) / 2, 2, ventanaArch.getLogicalUnitHeight(), 10, 0);

            /*********************************************************************
             * AGREGO OBJETOS QUE ENTRAN POR JSON (FILTRADOS)                     *
             *********************************************************************/

            //RECTANGLE
            vector<FileObjects>* fileRectangle = ventanaArch.getRectangle();
            vector<Rectangle*> vecRectangulos;
            //si tengo datos en el vector, entonces creo los elementos para imprimir
            if (fileRectangle->size() != 0) {
                for (int i = 0; i < fileRectangle->size(); i++) {
                    Rectangle *unRectangulo = new Rectangle(&objFact, fileRectangle->at(i).isStatic(), fileRectangle->at(i).getXCoordinate(), fileRectangle->at(i).getYCoordinate(), fileRectangle->at(i).getWidth(), fileRectangle->at(i).getHeight(), fileRectangle->at(i).getMass(), fileRectangle->at(i).getRotation());
                    vecRectangulos.push_back(unRectangulo);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay rectangulos cargados o no pasaron el filtro.\n");
            }

            //            //PARALLELLOGRAM
            vector<FileObjects>* fileParallellogram = ventanaArch.getParrallellogram();
            vector<Parallellogram*> vecParallellogram;
            if (fileParallellogram->size() != 0) {
                for (int i = 0; i < fileParallellogram->size(); i++) {
                    Parallellogram *unParalelogramo = new Parallellogram(&objFact, fileParallellogram->at(i).isStatic(), fileParallellogram->at(i).getXCoordinate(), fileParallellogram->at(i).getYCoordinate(), fileParallellogram->at(i).getBase(), fileParallellogram->at(i).getHighParall(), fileParallellogram->at(i).getAngle1(), fileParallellogram->at(i).getMass(), fileParallellogram->at(i).getRotation());
                    vecParallellogram.push_back(unParalelogramo);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay paralelogramos cargados o no pasaron el filtro.\n");
            }
            //            //TRAPEZIUM
            vector<FileObjects>* fileTrapezium = ventanaArch.getTrapezium();
            vector<Trapezium*> vecTrapezium;
            if (fileTrapezium->size() != 0) {
                for (int i = 0; i < fileTrapezium->size(); i++) {
                    Trapezium *unTrapezium = new Trapezium(&objFact, fileTrapezium->at(i).isStatic(), fileTrapezium->at(i).getXCoordinate(), fileTrapezium->at(i).getYCoordinate(), fileTrapezium->at(i).getBase(), fileTrapezium->at(i).getHeight(), fileTrapezium->at(i).getAngle1(), fileTrapezium->at(i).getAngle2(), fileTrapezium->at(i).getMass(), fileTrapezium->at(i).getRotation());
                    vecTrapezium.push_back(unTrapezium);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay trapecios cargados o no pasaron el filtro.\n");
            }
            //            //CIRCLE
            vector<FileObjects>* fileCircle = ventanaArch.getCircle();
            vector<Circle*> vecCircle;
            if (fileCircle->size() != 0) {
                for (int i = 0; i < fileCircle->size(); i++) {
                    Circle *unCircle = new Circle(&objFact, fileCircle->at(i).isStatic(), fileCircle->at(i).getXCoordinate(), fileCircle->at(i).getYCoordinate(), fileCircle->at(i).getScale(), fileCircle->at(i).getMass());
                    vecCircle.push_back(unCircle);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay circulos cargados o no pasaron el filtro.\n");
            }
            //            //TRIÁNGULO
            vector<FileObjects>* fileTriangulo = ventanaArch.getPolygon(3);
            vector<Polygon*> vecTriangulo;
            if (fileTriangulo->size() != 0) {
                //cout << "MASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA POLYGON:" << filePolygonFive->size() << endl;
                for (int i = 0; i < fileTriangulo->size(); i++) {
                    Polygon *unTriangulo = new Polygon(&objFact, fileTriangulo->at(i).isStatic(), fileTriangulo->at(i).getXCoordinate(), fileTriangulo->at(i).getYCoordinate(), fileTriangulo->at(i).getScale(), fileTriangulo->at(i).getSides(), fileTriangulo->at(i).getMass(), fileTriangulo->at(i).getRotation());
                    vecTriangulo.push_back(unTriangulo);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay triangulos cargados o no pasaron el filtro.\n");
            }
            //            //POLYGON de 5 lados
            vector<FileObjects>* filePolygonFive = ventanaArch.getPolygon(5);
            vector<Polygon*> vecPolygonFive;
            if (filePolygonFive->size() != 0) {
                for (int i = 0; i < filePolygonFive->size(); i++) {
                    Polygon *unPolygono = new Polygon(&objFact, filePolygonFive->at(i).isStatic(), filePolygonFive->at(i).getXCoordinate(), filePolygonFive->at(i).getYCoordinate(), filePolygonFive->at(i).getScale(), filePolygonFive->at(i).getSides(), filePolygonFive->at(i).getMass(), filePolygonFive->at(i).getRotation());
                    vecPolygonFive.push_back(unPolygono);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay poligonos (5) cargados o no pasaron el filtro.\n");
            }
            //
            //            //POLYGON de 6 lados
            vector<FileObjects>* filePolygonSix = ventanaArch.getPolygon(6);
            vector<Polygon*> vecPolygonSix;
            if (filePolygonSix->size() != 0) {
                //cout << "MASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA POLYGON:" << filePolygonSix->size() << endl;
                for (int i = 0; i < filePolygonSix->size(); i++) {
                    Polygon *unPolygono = new Polygon(&objFact, filePolygonSix->at(i).isStatic(), filePolygonSix->at(i).getXCoordinate(), filePolygonSix->at(i).getYCoordinate(), filePolygonSix->at(i).getScale(), filePolygonSix->at(i).getSides(), filePolygonSix->at(i).getMass(), filePolygonSix->at(i).getRotation());
                    vecPolygonSix.push_back(unPolygono);
                }
            } else {
                myLogFile->writeInLog(0, "[0] No hay poligonos (6) cargados o no pasaron el filtro.\n");
            }

            Character* mainCharacter = new Character(&objFact, ventanaArch.getPlayerXLocation(), ventanaArch.getPlayerYLocation());
            string lineToLog;
            lineToLog.append("[0] Se crearon ");
            lineToLog.append(to_string(world.GetBodyCount()));
            lineToLog.append(" elementos en el mundo.\n");
            myLogFile->writeInLog(0, lineToLog);
            float32 timeStep = 1.0f / 35.0f;
            int32 velocityIterations = 1;
            int32 positionIterations = 10;

            SDL_EventState(SDL_MOUSEMOTION, 0);
            while (!quit) {

                world.Step(timeStep, velocityIterations, positionIterations);
                world.ClearForces();
                SDL_RenderCopy(gRenderer, background->getTexture(), NULL, NULL);


                switch (mainCharacter->getState()) {
                    case 1://jumping
                        if (SDL_PollEvent(&event)) {
                            checkQuitOrReload(&event, &quit, &r);
                        }
                        if (event.key.state == SDL_RELEASED) {
                            switch (event.key.keysym.sym) {
                                case SDLK_LEFT:
                                    mainCharacter->stopWalking();
                                    break;
                                case SDLK_RIGHT:
                                    mainCharacter->stopWalking();
                                    break;
                            }
                        }
                        mainCharacter->keepJumping();
                        break;
                    case 2: //kneeling
                        if (SDL_PollEvent(&event)) {
                            checkQuitOrReload(&event, &quit, &r);
                        }
                        mainCharacter->keepKneeling();
                        break;
                    case 3: //fallingDown
                        if (SDL_PollEvent(&event)) {
                            checkQuitOrReload(&event, &quit, &r);
                            if (event.key.state == SDL_PRESSED) {
                                switch (event.key.keysym.sym) {
                                    case SDLK_LEFT:
                                        mainCharacter->fallLeft();
                                        break;
                                    case SDLK_RIGHT:
                                        mainCharacter->fallRight();
                                        break;
                                }
                            }
                            if (event.key.state == SDL_RELEASED) {
                                switch (event.key.keysym.sym) {
                                    case SDLK_LEFT:
                                        mainCharacter->stopWalking();
                                        break;
                                    case SDLK_RIGHT:
                                        mainCharacter->stopWalking();
                                        break;
                                }
                            }
                        }
                        mainCharacter->keepFallingDown();
                        break;
                    case 4: //walking
                        if (SDL_PollEvent(&event)) {
                            checkQuitOrReload(&event, &quit, &r);
                            if (event.key.state == SDL_PRESSED) {
                                switch (event.key.keysym.sym) {
                                    case SDLK_UP:
                                        mainCharacter->jump();
                                        break;
                                }
                            }
                            if (event.key.state == SDL_RELEASED) {
                                switch (event.key.keysym.sym) {
                                    case SDLK_LEFT:
                                        if (!mainCharacter->isWalkingRight()) {
                                            mainCharacter->stopWalking();
                                        }
                                        break;
                                    case SDLK_RIGHT:
                                        if (!mainCharacter->isWalkingLeft()) {
                                            mainCharacter->stopWalking();
                                        }
                                        break;
                                }
                            }
                        }
                        mainCharacter->keepWalking();
                        break;
                    case 5: // checkEvents
                        if (SDL_PollEvent(&event)) {
                            checkQuitOrReload(&event, &quit, &r);
                            if (event.key.state == SDL_PRESSED) {
                                switch (event.key.keysym.sym) {
                                    case SDLK_LEFT:
                                        mainCharacter->walkLeft();
                                        break;
                                    case SDLK_RIGHT:
                                        mainCharacter->walkRight();
                                        break;
                                    case SDLK_UP:
                                        mainCharacter->jump();
                                        break;
//                                    case SDLK_DOWN:
//                                        mainCharacter->kneel();
//                                        break;
                                    default:
                                        mainCharacter->wait();
                                        break;
                                }
                            } else {
                                mainCharacter->wait();
                            }
                        } else {
                            mainCharacter->wait();
                        }
                        break;
                }


                //                ground.draw(0xAA, 0xAA, 0xAA, 0xFF);
                //
                //                /*********************************************************************
                //                 * IMPRIMO TODOS LOS OBJETOS QUE ENTRARON POR JSON (FILTRADOS)       *
                //                 *********************************************************************/
                //
                //                //rectangle
                for (int i = 0; i < vecRectangulos.size(); i++) {
                    vecRectangulos.at(i)->draw(fileRectangle->at(i).getColorR(), fileRectangle->at(i).getColorG(), fileRectangle->at(i).getColorB(), fileRectangle->at(i).getColorA());
                }
                //
                //circle
                for (int i = 0; i < vecCircle.size(); i++) {
                    vecCircle.at(i)->draw(fileCircle->at(i).getColorR(), fileCircle->at(i).getColorG(), fileCircle->at(i).getColorB(), fileCircle->at(i).getColorA());
                }
                //                //TRIANGULO
                for (int i = 0; i < vecTriangulo.size(); i++) {
                    vecTriangulo.at(i)->draw(fileTriangulo->at(i).getColorR(), fileTriangulo->at(i).getColorG(), fileTriangulo->at(i).getColorB(), fileTriangulo->at(i).getColorA());
                }
                //                //POLYGON 5 
                for (int i = 0; i < vecPolygonFive.size(); i++) {
                    vecPolygonFive.at(i)->draw(filePolygonFive->at(i).getColorR(), filePolygonFive->at(i).getColorG(), filePolygonFive->at(i).getColorB(), filePolygonFive->at(i).getColorA());
                }
                //                //POLYGON 6
                for (int i = 0; i < vecPolygonSix.size(); i++) {
                    vecPolygonSix.at(i)->draw(filePolygonSix->at(i).getColorR(), filePolygonSix->at(i).getColorG(), filePolygonSix->at(i).getColorB(), filePolygonSix->at(i).getColorA());
                }
                //                //Trapezium                    
                for (int i = 0; i < vecTrapezium.size(); i++) {
                    vecTrapezium.at(i)->draw(fileTrapezium->at(i).getColorR(), fileTrapezium->at(i).getColorG(), fileTrapezium->at(i).getColorB(), fileTrapezium->at(i).getColorA());
                }
                //                PARALLELLOGRAM
                for (int i = 0; i < vecParallellogram.size(); i++) {
                    vecParallellogram.at(i)->draw(fileParallellogram->at(i).getColorR(), fileParallellogram->at(i).getColorG(), fileParallellogram->at(i).getColorB(), fileParallellogram->at(i).getColorA());
                }
                SDL_RenderPresent(gRenderer);
            }//en while general
            delete mainCharacter;

            for (int i = 0; i < vecRectangulos.size(); i++) {
                Rectangle *unRectangle = vecRectangulos.at(i);
                delete unRectangle;
            }
            //circle
            for (int i = 0; i < vecCircle.size(); i++) {
                Circle *unCircle = vecCircle.at(i);
                delete unCircle;
            }
            //                //TRIANGULO
            for (int i = 0; i < vecTriangulo.size(); i++) {
                Polygon *unPoligono = vecTriangulo.at(i);
                delete unPoligono;
            }
            //                //POLYGON 5 
            for (int i = 0; i < vecPolygonFive.size(); i++) {
                Polygon *unPoligono = vecPolygonFive.at(i);
                delete unPoligono;
            }
            //                //POLYGON 6
            for (int i = 0; i < vecPolygonSix.size(); i++) {
                Polygon *unPoligono = vecPolygonSix.at(i);
                delete unPoligono;
            }
            //                //Trapezium                    
            for (int i = 0; i < vecTrapezium.size(); i++) {
                Trapezium *unTrapecio = vecTrapezium.at(i);
                delete unTrapecio;
            }
            //                PARALLELLOGRAM
            for (int i = 0; i < vecParallellogram.size(); i++) {
                Parallellogram *unParalelogramo = vecParallellogram.at(i);
                delete unParalelogramo;
            }
        }//end if init
        delete background;
        delete gWindow;

        //elimino los objetos que ingresaron por JSON


        SDL_DestroyRenderer(gRenderer);
        gRenderer = NULL;
        IMG_Quit();
        SDL_Quit();
    }

    return r;
}

int main(int argc, char* args[]) {

    //    printf("inicia client 1\n");
    //    std::thread client1 (loadLevel, argc, args);
    //    printf("inicia client 2\n");
    //    std::thread client2 (loadLevel, argc, args);

    bool r = true;
    while (r) {
        r = loadLevel(argc, args);
    }
    //    
    //    printf("espera client 1\n");
    //    client1.join();
    //    printf("espera client 2\n");
    //    client2.join();
    //    
    return 0;

}//end main
